

int implicitReceiver( int rcvr, int method, int selector )
{
	return implicitReceiverForMixinImplementing( rcvr, methodClass(method), selector );
}

#define SuperclassIndex 1
#define MessageDictionaryIndex 2
#define EnclosingMixinIndex 4
#define MixinIndex 4
#define EnclosingObjectIndex 5
	
int implicitReceiverForMixinImplementing(int receiver, int mixin, int selector)
{
	int mixinApplication, dictionary, found;
	
	mixinApplication= findApplicationOfTargetMixinStartingAtBehavior( mixin, classOf(receiver) );

	if(mixinApplication == nil) return receiver;
		
	found= defines( slotOf(mixinApplication,MessageDictionaryIndex), selector );
	
	if(!found)
	{
		int implicitReceiverOrNil, theMixin;
		
		theMixin = slotOf(mixinApplication, MixinIndex);
		
		if(theMixin==nil)theMixin=mixinApplication; //they are the same for top level NS2
		//BOGUS!!!! Why does NSVM work without this?
		
		implicitReceiverOrNil = nextImplicitReceiverForWithMixin(
			slotOf(mixinApplication, EnclosingObjectIndex),
			slotOf(theMixin, EnclosingMixinIndex),
			selector);
		
		if( implicitReceiverOrNil != nil ) return implicitReceiverOrNil;
	}
	return receiver;
}

int findApplicationOfTargetMixinStartingAtBehavior(int targetMixin, int aBehavior)
{
	int mixinOrMixinApplication, mixin;
	
	mixinOrMixinApplication= aBehavior;
	
	for(;;){
		
		if(mixinOrMixinApplication == nil) break;
		if(mixinOrMixinApplication == targetMixin) break;
		mixin= slotOf(mixinOrMixinApplication, MixinIndex);
		if(mixin == targetMixin) break;
		if(classOf(mixin)==targetMixin) break;
		
		mixinOrMixinApplication= slotOf(mixinOrMixinApplication, SuperclassIndex);
	}			
				
	return mixinOrMixinApplication;
}

int nextImplicitReceiverForWithMixin(int anObject, int mixin, int selector)
{
	int implicitReceiver, mixinApplication, theMixin, targetMixin, dictionary, found;
	
	implicitReceiver = anObject;
	targetMixin = mixin;
	
	for(;;)
	{
		if(targetMixin == nil) return nil;
			
		mixinApplication = findApplicationOfTargetMixinStartingAtNonMetaClass(
			targetMixin, 
			classOf(implicitReceiver));
			
		if( mixinApplication == nil ) return nil;
		
		found = defines( slotOf(mixinApplication, MessageDictionaryIndex), selector );
		
		if(found) break;
		
		implicitReceiver= slotOf(mixinApplication, EnclosingObjectIndex);
		theMixin = slotOf(mixinApplication, MixinIndex);
		if(theMixin==nil) return nil;
		
		targetMixin = slotOf( theMixin, EnclosingMixinIndex );
	}
	
	return implicitReceiver;
}

int findApplicationOfTargetMixinStartingAtNonMetaClass(int targetMixin, int aClass)
{
	int mixinOrMixinApplication;
	mixinOrMixinApplication= aClass;
	
	for(;;)
	{
		if( mixinOrMixinApplication == nil ) break;
		if( mixinOrMixinApplication == targetMixin ) break;
		if( slotOf(mixinOrMixinApplication, MixinIndex) == targetMixin ) break;
		

		mixinOrMixinApplication= slotOf(mixinOrMixinApplication,SuperclassIndex);
	}
	
	return mixinOrMixinApplication;
}

int defines(int methodDict, int selector)
{
	int size = sizeOf(methodDict); //tally, array, keys
	
	for(int i=3; i<size; i++)
	{
		if( slotOf(methodDict,i)==selector) return 1;
	}
	return 0;
}




/*struct MethodCacheEntry
{
    int lookupClass,
        selector,
        method,
        primIndex,
        tempCount;
}*/
	
inline void push(int object)
{
	*++sp = object;
}

inline int pop()
{
	return *sp--;
}

inline int popNandPush(int n, int object)
{
	sp -= (n-1);
	*sp = object;
}

inline int peek(int offset)
{
	return *(sp-offset); 
}

inline int top() 
{ 
	return *sp;
}

void loadContextRegistersFrom(int newContext)
{
	//  MethodContext / BlockContext
	//1 sender			sender
	//2 pc				pc
	//3 sp				sp
	//4 method			nargs
	//5 closureOrNil	startpc
	//6 rcvr			home
	
	activeContext = newContext;
	if(classOf(newContext)==MethodContext) homeContext = newContext;
	else if(classOf(newContext)==BlockContext) homeContext = slotOf(newContext,6);
	else {
		PANIC("not a context!");
	}
	
	rcvr = slotOf(homeContext, 6);
	method = slotOf(homeContext, 4);
	
	pc = (unsigned char*)( method + 3 + unpackSmInt(slotOf(newContext,2)) );
	sp = (int*)( newContext + 4*(unpackSmInt(slotOf(newContext,3))+6) );	
}

void saveContextRegisters()
{
	setSlotOf(activeContext, 2, packSmInt( (int)pc - method - 3 ) );
	setSlotOf(activeContext, 3, packSmInt( ((int)sp - activeContext)/4 - 6 ) );
}

int firstCodePointerFor(int method)
{
	int header = *(int*)(method+4);
	int nWords = (header>>10)&255;
	
	return ((nWords+1)*4)+1;
}

void sendSpecial(int lobits)
{	
	//specialSelectors is  { ... sel, nArgs, sel, nArgs, ... }
	send( 
		slotOf(specialSelectors,(lobits*2)+1) ,
		unpackSmInt(slotOf(specialSelectors,(lobits*2)+2)) ,
		0 );
}

void send(int selector, int argc, int lookupType) //0=normal, 1=super, 2=dynamicsuper
{
	int newrcvr, class, mdict, array, size, i, start, o, z;
	
	//sendcount++;
	
	newrcvr = *(sp-argc);
	class = classOf(newrcvr);
	
#ifdef PRINT_SENDS
	//for(i=0;i<depth;i++) printf(" ");
#endif	
	//rcvrHistory[depth]=newrcvr;
	//selectorsHistory[depth]=selector;
	//depth++;
	//if(depth>historyDepth) PANIC("depth too great");		
	//lastSelector = selector;

#ifdef PRINT_SENDS
	printf("%s", classNameOf(newrcvr));
	printf(" %s", symbolToCString(selector));
	for(i=0;i<argc;i++) {printf(" "); printShortObject(peek(i));}
	printf("\n");
#endif	

#ifdef STEP_SENDS
	getchar();
#endif

    if(lookupType==1) {
		class = methodClass(method);
		class = slotOf(class, 1); //get superclass
	}
	if(lookupType==2){
	
		//printf("dynamic super\n");
			
		int mClassMixin = methodClass(method);
		int mixinApplication = findApplicationOfTargetMixinStartingAtBehavior(mClassMixin, classOf(rcvr));
		class = slotOf( mixinApplication, 1 ); //superclass		
	}
	
	int lookupClass = class;
	
	while(class != nil)
	{	
		mdict = slotOf(class,2);
		//size = sizeOf(mdict)-2;
		array = slotOf(mdict, 2);	
		size = sizeOf(array);
		
		start = (hashOf(selector) % size) + 1;
		
		for(i=start;i<=size;i++)
		{
			z = slotOf(mdict,i+2);
			if( z == selector ) {
				invoke( slotOf(array,i) , argc);
				return;
			}
			if( z == nil ) goto notfound;
		}
		for(i=1;i<start;i++)
		{
			z = slotOf(mdict,i+2);
			if( z == selector ) {
				invoke( slotOf(array,i) , argc);
				return;
			}
			if( z == nil ) goto notfound;
		}			
		notfound:
		
		class = slotOf(class,1); //superclass 
	}	
	
	if(selector==doesNotUnderstand) PANIC("could not deliver DNU");

	int argsArray = instantiateN(Array, argc);
	for(i=argc; i>0; i--)
		setSlotOf(argsArray, i, pop());
		
	int msg = instantiate(Message);
	printf("DNU: %s\n", symbolToCString(selector));
	setSlotOf(msg, 1, selector);
	setSlotOf(msg, 2, argsArray);
	setSlotOf(msg, 3, lookupClass);
	
	push(msg);
	send( doesNotUnderstand, 1, 0 ); //BOGUS : if this failed during a super send, should the DNU also be super?
	
}

void invoke(int newmethod, int argc)
{	
#ifdef TYPE_CHECK
	if( !(formatOf(newmethod)>=12 || formatOf(newmethod)<=15)) {
		printFullObject(newmethod);
		PANIC("NOT A METHOD!");
	}
#endif
	if(newmethod==nil) PANIC("cant invoke nil!");
	newMethod=newmethod;
	
	int primIndex = unpackSmInt(slotOf(newmethod,1))&0x300001FF;
	if(primIndex > 0x1FF) primIndex = (primIndex & 0x1FF) + (primIndex >> 19);
	if(primIndex != 0)
	{
		primitiveIndex = primIndex;
		argumentCount = argc;
		if( handlePrimitive() )
		{
			//depth--;
			return; //success => no need to execute this method
		}
	}
	
	
	//otherwise, we really need to set up a context and such
	
	int nextcontext, newrcvr, i, obj, tempCount, s;
	
	if( recycleContext != nil){
		//printString("recycle pop\n");
		
		nextcontext = recycleContext;
		recycleContext = slotOf(recycleContext,1);
		
		s = sizeOf(nextcontext);
		for(i=1;i<=s;i++) setSlotOf(nextcontext,i,nil);
	}else{
		nextcontext = instantiateN(MethodContext,57); //BOGUS: we should consult the method for the needed size
	}
	
	recyclableDepth++;
	
	for(i=0; i<=argc; i++) setSlotOf(nextcontext, 6+argc-i, newrcvr=pop());
	
	if( activeContext != nil ){
		saveContextRegisters();//Be sure to this after poping args from its stack!
	}
	
	tempCount = (unpackSmInt(slotOf(newmethod,1)) >> 18 ) & 63;
	setSlotOf(nextcontext, 1, activeContext);
	setSlotOf(nextcontext, 2, packSmInt(firstCodePointerFor(newmethod)));
	setSlotOf(nextcontext, 3, packSmInt(tempCount));
	setSlotOf(nextcontext, 4, newmethod);
	setSlotOf(nextcontext, 6, newrcvr);
		
	loadContextRegistersFrom(nextcontext);
	
	checkForInterrupts();//check for possible interrupts at each real send
}

void doReturn(int returnValue, int returnContext)
{
	int i;
	
	if(returnContext==nil) {
		printf("completed with ");
		printShortObject(returnValue);
		printf("\n");
		printFullObject(returnValue);
		exit(0);
	}
	
	if( classOf(activeContext)==MethodContext && recyclableDepth > 0){
		recyclableDepth--;
		
		if(sizeOf(activeContext)>55)
		{
			//printString("recycle push\n");
			setSlotOf(activeContext, 1, recycleContext);
			recycleContext = activeContext;
		}
	}
	
	if( classOf(activeContext)==BlockContext ) setSlotOf(activeContext,1,nil);
	
	loadContextRegistersFrom( returnContext );
	push(returnValue);

	//depth--;
#ifdef PRINT_RETURNS	
	for(i=0;i<depth;i++)printf(" ");
	printf("^");printShortObject(returnValue);printf("\n");
#endif 
}
